"""

.. module:: time_service
.. moduleauthor:: @author: 

*Description*:

This module contains the TimeService class.

*Constants*:

.. seealso:: class :class:`runtime.service.Service`
"""

# Imports
#
import datetime
import time
from threading import Timer
import logging

from datastore.resource import *
from datastore.uri import *
from runtime.service import *

# Constants
#

# Classes
#
class TimeService(Service):
    """
    **Constants**:
    """
    
    def cb_start(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        # res has also been saved as self.service_def
        self.logger.log(logging.DEBUG, "Timestamp service uri %s",
                        self.service_def.api.timestamp.uri)
        self.spaces.set_process(self.service_def.api.timestamp.uri,
                        self.cb_timestamp)
        self.spaces.set_watch(self.service_def.api.timer.uri,
                        self.cb_create_timer, filter=['put'])
#        self.spaces.set_watch(service_def.options.timer,
#                        self.cb_update_timer, filter=['update'])
        self.spaces.set_watch(self.service_def.api.timer.uri,
                        self.cb_delete_timer, filter=['delete', 'remove'])
        
    def cb_stop(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        # watch for our service being delete and remove any timers that
        # haven't yet pop'd.
        pass
    
    def cb_timestamp(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        if action == 'exists':
            return True
        elif action == 'get':
            try:
                self.logger.log(logging.DEBUG, "Timestamp service called.")
                timestamp_res = Resource(
                    type=self.service_def.api.timestamp.response)
                timestamp_res.timestamp = \
                    str(datetime.datetime.now().isoformat())
                return timestamp_res
            except:
                exc_info = sys.exc_info()
                self.logger.exception("Timestamp service error. " + \
                    "Exception: %s. Value: %s." % (exc_info[0], exc_info[1]))
            return None
    
    def cb_create_timer(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        # ignore the put/update for the response (and anything else)
        if res.get_type() == Uri(self.service_def.api.timer.request):
            if action in ['put', 'update']:
                self.set_timer(uri, res)

#    def cb_update_timer(self, uri, action, res):
#        if action == 'update':

    def cb_delete_timer(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        # res is not available for a delete.
        del_res = self.spaces.get(uri)
        if action == 'delete':
            if del_res.get_type() == \
                            Uri(self.service_def.api.timer.request):
                del_res.timer.cancel()

    def timer_pop(self, uri, req_res):
        """
        :param uri: Uri
        :param req_res:
        """
        
        self.logger.log(logging.DEBUG, "Timer pop (%s), uri (%s)",
                        req_res.get_var('dc:title'), uri)

        # Each new thread gets its own database connection.
        self.datastore = DataStore(self.datastore.filename)

        res = Resource(type=self.service_def.api.timer.response)
        res.timestamp = str(datetime.datetime.now())
        if not req_res.has_var('iterations'):
            req_res.iterations = 1
        if req_res.iterations > 1:
            req_res.iterations = req_res.iterations - 1
            self.set_timer(uri, req_res)
        res.iterations = req_res.iterations
        
        self.logger.log(logging.DEBUG, "Timer pop - put() to uri (%s)", uri)
        
        self.spaces.put(uri + 'response', res)

    def set_timer(self, uri, res):
        """
        :param uri: Uri
        :param res:
        """
        
        if res.has_var('interval'):
            interval = res.interval
        else:
            interval = 0
        # zero is changed to a very small value to ensure we still run the
        # callback.
        if res.has_var('timestamp'):
            tss = res.timestamp
            for format in ['%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M:%S.%f',
                           '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f']:
                try:
                    tsd = datetime.datetime.strptime(tss, format)
                    break
                except:
                    tsd = None
            if tsd is None:
                self.logger.log(logging.DEBUG, 
                    "Invalid timestamp (%s) for time, interval set to '0'",
                    tss)
            else:
                # calculation performed using unix time for simplicity
                unix_time = time.mktime(tsd.timetuple()) + tsd.microsecond/1e6
                interval =  unix_time - time.time()
                if interval < 0:
                    interval = 0
                    self.logger.log(logging.DEBUG, 
                        "Timestamp (%s) in the past, interval set to '0'",
                        tss)

        self.logger.log(logging.DEBUG, "Set timer interval (%f).", interval)
        if interval == 0:
            interval = 0.00001
        timer = Timer(interval, self.timer_pop, args=[uri, res])
        res.timer = timer
        timer.name = "[timer] %s" % res.get_var('dc:title')
        timer.start()

